package com.netease.xone.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;

import com.netease.log.NTLog;
import com.netease.xone.R;

public class NPullToFreshContainer extends FrameLayout implements Animation.AnimationListener{
	private static final boolean DEBUG = false;
	
	private int HEAD_VIEW_HEIGHT = 0;
	private final int STATE_RESET = 0;
	private final int STATE_PULL_TO_REFRESH = 1;
	private final int STATE_RELEASE_TO_REFRESH = 2;
	private final int STATE_REFRESHING = 3;
	private final int STATE_REFRESHING_SCROLL = 4;
	private int mState = STATE_RESET ;
	private ImageView mRefreshViewImage;
	private TextView mText;
	private TextView mDateTv;
	private RotateAnimation  mAnimationDown;
	private RotateAnimation  mAnimationUp;
	private View mRefreshView;
	private float mLastMotionY;
	private int mPadding;
	private ProgressBar mProgressBar;
	private MotionEvent mCurrentDownEvent;
	private OnContainerRefreshListener mOnRefreshListener;
	private Flinger mFling;
	private String mDate;
	private boolean mFirstLayout = true;
	private int originalTop = 0;
	
	private void init(Context context) {
		setDrawingCacheEnabled(false);
		mRefreshView = LayoutInflater.from(getContext()).inflate(R.layout.pull_to_refresh_header, null);
		
		mRefreshView.setVisibility(View.INVISIBLE);
		mRefreshView.setBackgroundColor(0xf7f7f8);
		mRefreshViewImage = (ImageView)mRefreshView.findViewById(R.id.pull_to_refresh_image);
		mRefreshViewImage.setScaleType(ImageView.ScaleType.FIT_CENTER);
		float density = context.getResources().getDisplayMetrics().density;
		mRefreshViewImage.setMinimumHeight((int)(50 * density));
		
		mText = (TextView)mRefreshView.findViewById(R.id.pull_to_refresh_text);
		mDateTv = (TextView)mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);
		mDateTv.setVisibility(View.VISIBLE);
		mProgressBar = (ProgressBar)mRefreshView.findViewById(R.id.pull_to_refresh_progress);
		
		
		mAnimationUp = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);        
		mAnimationUp.setInterpolator(new LinearInterpolator());        
		mAnimationUp.setDuration(250);        
		mAnimationUp.setFillAfter(true);
		mAnimationUp.setAnimationListener(this);
		
		mAnimationDown = new RotateAnimation(-180, 0,                
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,                
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);        
		mAnimationDown.setInterpolator(new LinearInterpolator());        
		mAnimationDown.setDuration(250);        
		mAnimationDown.setFillAfter(true);
	   
	    //add headView
		addView(mRefreshView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
		mFling = new Flinger();
	}

	public NPullToFreshContainer(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		init(context);
	}

	public NPullToFreshContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		init(context);
	}

	public NPullToFreshContainer(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		init(context);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		// TODO Auto-generated method stub
		getChildAt(0).layout(0, -HEAD_VIEW_HEIGHT - mPadding, getMeasuredWidth(), -mPadding);
		getChildAt(1).layout(0, -mPadding, getMeasuredWidth(), getMeasuredHeight());
		if (mFirstLayout || HEAD_VIEW_HEIGHT == 0) {
			HEAD_VIEW_HEIGHT = getChildAt(0).getMeasuredHeight();
			final AdapterView adapter = (AdapterView)getChildAt(1);
			if (adapter.getCount() > 0) {
				if (adapter.getChildAt(0) != null) {
					originalTop = adapter.getChildAt(0).getTop();
				}
			}
			mFirstLayout = false;
		}
		
	}
	
	@Override
	protected void dispatchDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.dispatchDraw(canvas);
		View headView = getChildAt(0);
		View contentView = getChildAt(1);
		switch (mState) {
		case STATE_RESET:
			headView.setVisibility(View.INVISIBLE);
			contentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
			break;
		case STATE_PULL_TO_REFRESH:
		case STATE_RELEASE_TO_REFRESH:
			headView.setVisibility(View.VISIBLE);
			int offset = -mPadding - contentView.getTop();
			contentView.offsetTopAndBottom(offset);
			headView.setVisibility(View.VISIBLE);
			mProgressBar.setVisibility(View.INVISIBLE);
			// -HEAD_VIEW_HEIGHT - mPadding = new Headview top coordinate
			headView.offsetTopAndBottom(-HEAD_VIEW_HEIGHT - mPadding
					- headView.getTop());

			if (mState == STATE_PULL_TO_REFRESH) {
				mText.setText(R.string.pull_to_fresh_text);
			} else {
				mText.setText(R.string.release_to_fresh_text);
			}
			if (mDate != null) {
				mDateTv.setText(mDate);
			}
			break;
		case STATE_REFRESHING:
			headView.setVisibility(View.VISIBLE);
			contentView.offsetTopAndBottom(-mPadding - contentView.getTop());
			final int top = headView.getTop();
			contentView.layout(contentView.getLeft(), contentView.getTop(),
					contentView.getRight(), getHeight());
			mProgressBar.setVisibility(View.VISIBLE);
			mRefreshViewImage.clearAnimation();
			mRefreshViewImage.setVisibility(View.INVISIBLE);
			mText.setText(R.string.list_refreshing_text);
			if (mDate != null) {
				mDateTv.setText(mDate);
			}
			headView.offsetTopAndBottom(-HEAD_VIEW_HEIGHT - mPadding - top);
			break;
		case STATE_REFRESHING_SCROLL:
			contentView.invalidate();
			contentView.offsetTopAndBottom(-mPadding - contentView.getTop());
			final int top1 = headView.getTop();
			contentView.layout(contentView.getLeft(), contentView.getTop(),
					contentView.getRight(), getHeight());
			mProgressBar.setVisibility(View.VISIBLE);
			mDateTv.setVisibility(View.VISIBLE);
			mText.setText(R.string.list_refreshing_text);
			headView.offsetTopAndBottom(-HEAD_VIEW_HEIGHT - mPadding - top1);
			break;
		}
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		if (mIsAutoScroller) {
			ev.setAction(MotionEvent.ACTION_CANCEL);
			super.dispatchTouchEvent(ev);
			return true;
		}
		
		final int action = ev.getAction() & MotionEvent.ACTION_MASK;
		final AdapterView adapterView = (AdapterView) getChildAt(1);
		final Adapter adapter = adapterView.getAdapter();
		if (adapter == null || adapter.getCount()== 0) {
			return true;
		}
		
		final float y = ev.getY();
		final float x = ev.getX();
		
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			mLastMotionY = y;

			if (mCurrentDownEvent != null) {
				mCurrentDownEvent.recycle();
			}
			mCurrentDownEvent = MotionEvent.obtain(ev);
			break;
		case MotionEvent.ACTION_MOVE:
			final float scrollY = mLastMotionY - y;

			mLastMotionY = y;
			final int position = adapterView.getFirstVisiblePosition();
			int top = 0;
			if (adapterView.getChildCount() > 0) {
				top = adapterView.getChildAt(0).getTop();
			}
			
			if (mState == STATE_REFRESHING ) {
				final float deltaY = mCurrentDownEvent.getY() - y;
				if (deltaY <= 0) {
					return super.dispatchTouchEvent(ev);
				} else {
					mState = STATE_REFRESHING_SCROLL;
				}
			}
			
			if (mState == STATE_REFRESHING_SCROLL) {
				final int newPadding = mPadding + (int)(scrollY/1.7f);	
				
				if (position == 0 && mPadding == -HEAD_VIEW_HEIGHT && scrollY < 0) {
					return super.dispatchTouchEvent(ev);
				} else if (newPadding > -HEAD_VIEW_HEIGHT && newPadding < 0 && mPadding < 0) {
					mPadding = newPadding;
					invalidate();
					return true;
				} else if (newPadding > 0 && mPadding < 0) {
					mPadding = 0;
					invalidate();
					return true;
				}
				NTLog.v("dispatchevent", "top: " + top);
				if (position == 0 && mPadding == 0 && scrollY < 0 && top == originalTop) {
					NTLog.v("dispatchevent", "position == 0 && mPadding == 0 && scrollY < 0: " + " newPadding: " + newPadding);
					if (newPadding < 0) {
						mPadding = newPadding;
						invalidate();
						return true;
					}
				} else if (position == 0 && newPadding < 0 && top == originalTop) {
					mPadding = newPadding;
					invalidate();
					return true;
				} else if (position == 0 && newPadding > 0) {
					mPadding = 0;
					//invalidate();
//					return true;
				} 
				if (DEBUG) NTLog.v("dispatchTouchEvent", "mPadding: " + mPadding + "newPadding: " + newPadding + "position: " + position);
				
				return super.dispatchTouchEvent(ev);
			}
			if (DEBUG) NTLog.v("dispatchTouchEvent", "dispatchTouchEvent mstate: " + mState + "mPadding: " + mPadding + "position: " + position 
					+ " top: " + adapterView.getTop());
			if (position == 0
					&& ((top == originalTop && scrollY < 0)|| (adapterView.getTop() > 0))) {
				mPadding += (int) scrollY / 2;
				if (mPadding > -HEAD_VIEW_HEIGHT
						&& mPadding < 0
						&& (mState == STATE_RESET
								|| mState == STATE_PULL_TO_REFRESH || mState == STATE_RELEASE_TO_REFRESH)) {
					mProgressBar.setVisibility(View.INVISIBLE);
					mRefreshViewImage.setVisibility(View.VISIBLE);
					
					if (mState == STATE_RELEASE_TO_REFRESH) {
						mRefreshViewImage.clearAnimation();
						mRefreshViewImage.startAnimation(mAnimationDown);
					}

					mState = STATE_PULL_TO_REFRESH;
				} else if (mPadding <= -HEAD_VIEW_HEIGHT
						&& (mState == STATE_PULL_TO_REFRESH)) {
					mProgressBar.setVisibility(View.INVISIBLE);
					mRefreshViewImage.setVisibility(View.VISIBLE);
					mRefreshViewImage.clearAnimation();
					mRefreshViewImage.startAnimation(mAnimationUp);
					mState = STATE_RELEASE_TO_REFRESH;
				}
				invalidate();
				if (mPadding != 0) {
					ev.setAction(MotionEvent.ACTION_CANCEL);
				}
				return super.dispatchTouchEvent(ev);
			}
			
			if (mState ==  STATE_PULL_TO_REFRESH || mState == STATE_RELEASE_TO_REFRESH) {
				ev.setAction(MotionEvent.ACTION_CANCEL);
				return super.dispatchTouchEvent(ev);
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			if (DEBUG) NTLog.v("dispatchTouchEvent", "Action_up" + mState + "mPadding: " + mPadding + " ev.getaction: " + ev.getAction());
			int pos = adapterView.getFirstVisiblePosition();
			if (pos == 0 && adapterView.getTop() > 0 && mState == STATE_RELEASE_TO_REFRESH) {
				mState = STATE_REFRESHING;
				scrollToUpdate();
				onRefresh();
				ev.setAction(MotionEvent.ACTION_CANCEL);
			} else if (mState == STATE_REFRESHING) {
				scrollToUpdate();
//				ev.setAction(MotionEvent.ACTION_CANCEL);
			} else if (mState == STATE_REFRESHING_SCROLL) {
				final int bottom = getChildAt(0).getBottom();
				if (bottom >= HEAD_VIEW_HEIGHT) {
					scrollToUpdate();
					ev.setAction(MotionEvent.ACTION_CANCEL);
					return super.dispatchTouchEvent(ev);
				} else if (bottom > 0 && bottom < HEAD_VIEW_HEIGHT){
					scrollToClose();
					ev.setAction(MotionEvent.ACTION_CANCEL);
					return super.dispatchTouchEvent(ev);
				}
			} else {
				scrollToClose();
				mState = STATE_RESET;
			}
			break;
		}
		
		if (DEBUG) NTLog.v("dispatchTouchEvent", "dispatchTouchEvent mstate: " + mState + "mPadding: " + mPadding + " ev.getaction: " + ev.getAction());
		return super.dispatchTouchEvent(ev);
	}

	@Override
	public void onAnimationStart(Animation animation) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onAnimationEnd(Animation animation) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onAnimationRepeat(Animation animation) {
		// TODO Auto-generated method stub
		
	}
	
	private boolean mIsAutoScroller;
	
	private class Flinger implements Runnable {
		private boolean mIsStart;
		private int mLastFlingX;
		private Scroller mScroller;

		public Flinger() {
			mScroller = new Scroller(getContext());
		}

		private void startCommon() {
			removeCallbacks(this);
		}

		public void run() {
			boolean more = mScroller.computeScrollOffset();
			if (more) {
				mPadding += mLastFlingX - mScroller.getCurrX();
				NPullToFreshContainer.this.invalidate();
				mLastFlingX = mScroller.getCurrX();
				removeCallbacks(this);
				post(this);
			} else {
				mIsAutoScroller = false;
			}
		}

		public void startUsingDistance(int distance, int duration) {
			startCommon();
			mLastFlingX = 0;
			
			mScroller.startScroll(0, 0, -distance, 0, duration);
			mIsAutoScroller = true;
			post(this);
		}
	}
	
	public void onComplete(final String date) {
		endUpdate(date);
	}
	
	public void endUpdate(String date) {
		setUpdateDate(date);
		
		if (mPadding != 0) {
			scrollToClose();
		}
		mState = STATE_RESET;
	}
	
	public void setUpdateDate(String date){
		if (date == null) {
			return;
		}
		mDate = date;
	}
	
	public void doRefresh() {
		mState = STATE_REFRESHING;
		scrollToUpdate();
		onRefresh();
	}
	
	private void scrollToUpdate() {
		mFling.startUsingDistance(-mPadding -HEAD_VIEW_HEIGHT, 250);
	}
	
	private void scrollToClose() {
		if (mPadding == 0) {
			return;
		}
		mFling.startUsingDistance(-mPadding, 250);
	}
	
	/**
     * Register a callback to be invoked when this list should be refreshed.
     * 
     * @param onRefreshListener The callback to run.
     */
    public void setOnRefreshListener(OnContainerRefreshListener onRefreshListener) {
        mOnRefreshListener = onRefreshListener;
    }

	public void onRefresh() {
		if (mOnRefreshListener != null) {
			mOnRefreshListener.onContainerRefresh();
		}
	}

	public interface OnContainerRefreshListener {
		/**
		 * * Called when the list should be refreshed. *
		 * <p>
		 * * A call to {@link PullToRefreshListView #onRefreshComplete()} is *
		 * expected to indicate that the refresh has completed.
		 */
		public void onContainerRefresh();
	}
}
